package com.syyo.gulimall.order.config.rabbit;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.PostConstruct;

/**
 * @ author: wang
 * @ date: 2021/4/22 11:29
 * @ Description: 消息确认的回调配置
 */
@Configuration
public class MyRabbitConfig {

    private RabbitTemplate rabbitTemplate;

    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter());
        initRabbitTemplate();
        return rabbitTemplate;
    }

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    /**
     *
     * PostConstruct 标注了这个注解的方法，会在这个对象创建完成之后调用这个方法
     *
     * ready：还没进入到channel  unacked：消息进入到channel，状态为未签收
     * 生产者确认，设置回调；
     *    1.确认回调
     *    2.消息正确抵达队列回调
     *
     *
     * 消费者确认
     *    1.默认自动确认，
     *       问题，服务器宕机，剩下的消息全部丢失
     *    2.手动确认：只要没有确认，消息一直是unacked的状态
     *        确认消息，收货
     *        channel.basicAck(deliveryTag,false);
     *        拒接，退货
     *        channel.basicNack(deliveryTag,false,false);
     *
     */
    public void initRabbitTemplate(){
        // 设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 当前消息的唯一关联数据
             * @param ack 消息是否成功收到,只要消息到达broker，一定返回true
             * @param cause 失败的原因
             *
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                /**
                 * 消息丢失的解决方法：
                 * 1.做好消息确认机制（生成者，消费者【手动确认】）
                 * 2.每一个发送的消息都存在数据库，定期将失败的消息重复发送
                 */
                // 消息已存到队列，修改数据库消息的状态为已发送
                System.out.println("==========confirm===========");
                System.out.println("OK correlationData="+correlationData+"  ack="+ack+"  cause="+cause);
            }
        });

        // 消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback(){
            /**
             *  只要消息没有传给队列，就会触发这个方法
             * @param message 投递失败消息的详情
             * @param replyCode 回复的状态码
             * @param replyText 回复的内容
             * @param exchange 消息对应的交换机
             * @param routingKey  消息对应的路由key
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                // 报错， 消息没存到队列，修改数据库消息的状态为未发送
                System.out.println("==========returned===========");
                System.out.println("ERROR message=" + message+"  replyCode=" + replyCode + "  replyText=" + replyText + "  exchange=" + exchange + "  routingKey=" + routingKey);
            }
        });
    }
}
